전역 객체 싱글톤 패턴은 객체가 추가로 생성(인스턴스화)되는 것을 막는 장치가 없다.
(static으로 선언한다고 해도, 인스턴스가 추가로 생성되는 것은 허용됨)
객체가 추가로 생성되는 것을 막기 위해, 생성자 안에 static 카운터 변수를 두고 값이 증가될 경우 익셉션(예외)
를 발생
struct Database{
Database(){
static int instance_count{0};
if(++instance_count>1)
throw std::exception("Cannot make >1 database!");
}
};
위의 방법은 사용자 소통 측면에서 친밀하지 못한 구현이다.
Database 생성자를 private로 선언하고,
인스턴스를 리턴받기 위한 멤버 함수(하나만 존재하는 인스턴스를 리턴)를 구현
struct Database{
protected:
Database(){
}
public:
static Database& get(){
static Database database;
return database;
}
Database(Database const&)=delete;
Database(Database&&)=delete;
Database& operator=(Database const&)=delete;
Database& operator=(Database&&)=delete;
};
복사/이동/연산자를 수작업으로 제거하는 대신, boost::noncopyable 클래스를 상속받아 구현하면,
이동 생성자/연산자를 제외하고 모두 숨길 수 있다.
Database 클래스가 다른 static 혹은 전역 객체에 종속적이고,
소멸자가 static 혹은 전역 객체를 참조하고 있는 경우 위험하다.
(static 객체, 전역 객체의 초기화 소멸 순서는 결정적이지 않기 때문에 접근하는 시점에 소멸되어 있을 수 있다.)
get() 메서드 내에서 static 객체를 스택에 할당하는 것이 아닌 힙에 할당
(전체 객체가 아닌 포인터만 static으로 존재함)
static Database& get(){
static Database* database=new Database();
return *database;
}
힙에 할당한 메모리는 처음 할당되 이후, 프로그램이 종료될 때 까지 소멸자가 호출되지 않음
(static으로 선언된 객체(변수)의 초기화는 전체 런타임 중 한 번만 수행된다)